
#include "StdAfx.h"
#define MYLIB_API_EXPORT
#include "MorphologicalProc.h"
#include "MeshSimplification.h"
#include "KernelConverter.h"

//initialize the static member
std::auto_ptr<CMorphologicalProc> CMorphologicalProc::_instance(0);

CMorphologicalProc::CMorphologicalProc(void)
{
}


CMorphologicalProc::~CMorphologicalProc(void)
{
}

void CMorphologicalProc::ExtentRange(double& Min,double& Max,double scale)
{
	double lengh = (Max-Min)*scale;
	double cen = (Min+Max)*0.5;
	Min = cen-lengh*0.5;
	Max = cen+lengh*0.5;
}

void CMorphologicalProc::CalBoundingBox(const EK_Poly_3* pSolid, EK_Poly_3& poly, double scale)
{
	//Get the coords
	double xMin, xMax, yMin, yMax, zMin, zMax;
	EK_Poly_3::Point_const_iterator ptitr = pSolid->points_begin();

	xMax = CGAL::to_double((*ptitr).x());
	xMin = CGAL::to_double((*ptitr).x());
	yMax = CGAL::to_double((*ptitr).y());
	yMin = CGAL::to_double((*ptitr).y());
	zMax = CGAL::to_double((*ptitr).z());
	zMin = CGAL::to_double((*ptitr).z());

	for (;ptitr  != pSolid->points_end(); ++ptitr )
	{
		if (CGAL::to_double((*ptitr ).x())>xMax)
		{
			xMax = CGAL::to_double((*ptitr ).x());
		}
		else if (CGAL::to_double((*ptitr ).x())<xMin)
		{
			xMin = CGAL::to_double((*ptitr ).x());
		}
		//
		if (CGAL::to_double((*ptitr ).y())>yMax)
		{
			yMax = CGAL::to_double((*ptitr ).y());
		}
		else if (CGAL::to_double((*ptitr ).y())<yMin)
		{
			yMin = CGAL::to_double((*ptitr ).y());
		}
		//
		if (CGAL::to_double((*ptitr ).z())>zMax)
		{
			zMax = CGAL::to_double((*ptitr ).z());
		}
		else if (CGAL::to_double((*ptitr ).z())<zMin)
		{
			zMin = CGAL::to_double((*ptitr ).z());
		}
	}
	//scale model a bit to make it bigger than the bounding box
	ExtentRange(xMin,xMax,scale);
	ExtentRange(yMin,yMax,scale);
	ExtentRange(zMin,zMax,scale);
	//Create the polyhedron
	typedef EK_Poly_3::Point_3 Point;
	typedef EK_Poly_3::Halfedge_handle Halfedge_handle;
	Halfedge_handle h = poly.make_tetrahedron(Point(xMax,yMin,zMin),
		Point(xMin,yMin,zMax),
		Point(xMin,yMin,zMin),
		Point(xMin,yMax,zMin));
	Halfedge_handle g = h->next()->opposite()->next();
	//
	poly.split_edge( h->next());
	poly.split_edge( g->next());
	poly.split_edge( g);
	//
	h->next()->vertex()->point() = Point( xMax, yMin, zMax);
	g->next()->vertex()->point() = Point( xMin, yMax, zMax);
	g->opposite()->vertex()->point() = Point( xMax, yMax, zMin); //
	//
	Halfedge_handle f = poly.split_facet( g->next(),
		g->next()->next()->next()); //
	//
	Halfedge_handle e = poly.split_edge( f);
	e->vertex()->point() = Point( xMax, yMax, zMax); //
	//
	poly.split_facet( e, f->next()->next()); //
	//
	CGAL_postcondition( poly.is_valid());
}

void CMorphologicalProc::GenPoly(double size, EK_Poly_3& poly)
{
	double xMin = 0.0;
	double yMin = 0.0;
	double zMin = 0.0;
	double xMax = size;
	double yMax = size;
	double zMax = size;
	//Create the polyhedron
	typedef EK_Poly_3::Point_3 Point;
	typedef EK_Poly_3::Halfedge_handle Halfedge_handle;
	Halfedge_handle h = poly.make_tetrahedron(Point(xMax,yMin,zMin),
		Point(xMin,yMin,zMax),
		Point(xMin,yMin,zMin),
		Point(xMin,yMax,zMin));
	Halfedge_handle g = h->next()->opposite()->next();
	//
	poly.split_edge( h->next());
	poly.split_edge( g->next());
	poly.split_edge( g);
	//
	h->next()->vertex()->point() = Point( xMax, yMin, zMax);
	g->next()->vertex()->point() = Point( xMin, yMax, zMax);
	g->opposite()->vertex()->point() = Point( xMax, yMax, zMin); //
	//
	Halfedge_handle f = poly.split_facet( g->next(),
		g->next()->next()->next()); //
	//
	Halfedge_handle e = poly.split_edge( f);
	e->vertex()->point() = Point( xMax, yMax, zMax); //
	//
	poly.split_facet( e, f->next()->next()); //
	//
	CGAL_postcondition(poly.is_closed());
}

Nef_polyhedron_3* CMorphologicalProc::MinKowskisum(Nef_polyhedron_3* poly, double operSize /* = 1.0 */)
{
	//construct the operator
	EK_Poly_3 operModel;
	GenPoly(operSize, operModel);
	if (operModel.size_of_facets() == 0||!operModel.is_closed())
	{
		return NULL;
	}
	Nef_polyhedron_3 operatorModel(operModel);
	//minkowIKi sum
	CGAL_assertion(poly->number_of_vertices() != 0);
	std::cout<<"++minkowski sum start"<<std::endl;
	Nef_polyhedron_3* result  = new Nef_polyhedron_3(CGAL::minkowski_sum_3(*poly,operatorModel));
	//translate to the proper postion
	double dlength = -operSize*0.5;
	Nef_polyhedron_3::Aff_transformation_3 trans(CGAL::TRANSLATION,Vector_3(dlength,dlength,dlength));
	result->transform(trans);
	//it could reduce the vertices
	*result = result->regularization();
	//post process
	std::cout<<"after minkowski sum "<<Simplify_func::GetSingleton()->clean_nef_polyhedron(*result)<<std::endl;
	if (!result->is_simple())
	{
		std::cout<<"minkowski sum cause non-2-manifold case"<<std::endl;
		delete result;
		result = NULL;
	}
	else
	{
		if(poly)
			delete poly;
		poly = NULL;
		std::cerr<<"++minkowski sum finished"<<std::endl;
	}
	return result;
}

Nef_polyhedron_3* CMorphologicalProc::ReversMinKowskisum(Nef_polyhedron_3* poly, double operSize /* = 1.0 */)
{
	//construct the operator
	EK_Poly_3 operModel;
	GenPoly(operSize, operModel);
	if (operModel.size_of_facets() == 0||!operModel.is_closed())
	{
		return NULL;
	}
	Nef_polyhedron_3 operatorModel(operModel);
	//minkowIKi sum
	CGAL_assertion(poly->number_of_vertices() != 0);
	std::cout<<"--minkowski diff start"<<std::endl;
	//compute the bounding polyhedron
	EK_Poly_3 poly_bound;
	EK_Poly_3 tmp_poly;
	if (!poly->is_simple())
	{
		CGAL_assertion(0);
		return NULL;
	}
	poly->convert_to_polyhedron(tmp_poly);
	CalBoundingBox(&tmp_poly,poly_bound,100.0);
	Nef_polyhedron_3 modelA(poly_bound);
	//
	*poly = poly->regularization();
	Nef_polyhedron_3 modelB = *poly;
	//
	Nef_polyhedron_3 modelC = modelA - modelB;
	modelC = modelC.regularization(); 
	//
	Nef_polyhedron_3 modelDall = CGAL::minkowski_sum_3(modelC,operatorModel);

	modelDall = modelDall.regularization();
	//translate the result to the center of original model
	double dlength = -operSize*0.5;
	Nef_polyhedron_3::Aff_transformation_3 trans(CGAL::TRANSLATION,Vector_3(dlength,dlength,dlength));
	modelDall.transform(trans);
	//
	Nef_polyhedron_3* test = new Nef_polyhedron_3(modelB - modelDall);
	//it could reduce the vertices
	*test = test->regularization();
	//post process
	std::cout<<"after minkowIKisum "<<Simplify_func::GetSingleton()->clean_nef_polyhedron(*test)<<std::endl;
	if (!test->is_simple())
	{
		std::cerr<<"minkowski diff cause non-2-manifold case"<<std::endl;
		delete test;
		test = NULL;
	}
	else
	{
		if(poly)
			delete poly;
		poly = NULL;
		std::cerr<<"--minkowski diff finished"<<std::endl;
	}
	return test;
}


void CMorphologicalProc::minkowski_closing(Nef_polyhedron_3* poly, double operSize /* = 1.0 */)
{
	if (poly == NULL)
	{
		return;
	}
	poly = MinKowskisum(poly,operSize);
	if (poly == NULL)
	{
		return;
	}
	poly = ReversMinKowskisum(poly,operSize);
	if (poly == NULL)
	{
		return;
	}
}

void CMorphologicalProc::minkowski_opening(Nef_polyhedron_3* poly, double operSize /* = 1.0 */)
{
	if (poly == NULL)
	{
		return;
	}
	poly = ReversMinKowskisum(poly,operSize);
	if (poly == NULL)
	{
		return;
	}
	poly = MinKowskisum(poly,operSize);
	if (poly == NULL)
	{
		return;
	}
}



bool CMorphologicalProc::minkowski_closing(ModelOBJ* model, double operSize)
{

	if(!MinKowskisum(model,operSize))
		return false;
	if(!ReversMinKowskisum(model,operSize))
		return false;
	return true;
}

bool CMorphologicalProc::minkowski_opening(ModelOBJ* model, double operSize /* = 1.0 */)
{
	if(!ReversMinKowskisum(model,operSize))
		return false;
	if(!MinKowskisum(model,operSize))
		return false;
	return true;
}

bool CMorphologicalProc::ReversMinKowskisum(ModelOBJ* model, double operSize/* = 1.0*/)
{
	int Solid_no = 0;
	SOLID_itr solid_itr = model->begin();
	while (solid_itr != model->end())
	{
		SolidOBJ* solid = *solid_itr++;
		if (solid->Solid_mesh->size_of_vertices() == 0 || !solid->Solid_mesh->is_closed())
		{
			continue;
		}

		//kernel conversion IK to EK
		EK_Poly_3 ek_poly;
		if (!CKernelConverter::GetSingleton()->IK_to_EK(solid->Solid_mesh, &ek_poly))
		{
			return false;
		}

		//
		Nef_polyhedron_3* result = new Nef_polyhedron_3(ek_poly);

		std::cout<<"Solid:"<<Solid_no++<<std::endl;
		result = ReversMinKowskisum(result,operSize);

		if (!result)
		{
			return false;
		}

		if (result->is_simple())
		{
			result->convert_to_polyhedron(ek_poly);
			
			//kernel conversion EK to IK
			Polyhedron_3 polycpy;
			if (!CKernelConverter::GetSingleton()->EK_to_IK(&ek_poly, &polycpy))
			{
				return false;
			}
			//
			Polyhedron_3* tmp = solid->Solid_mesh;
			Polyhedron_3* poly = new Polyhedron_3(polycpy);
			solid->Solid_mesh = poly;
			delete tmp;
			tmp = NULL;
		}
		else
		{
			std::cerr<<"minkowski diff cause non-2-manifold case"<<std::endl;
		}
		//
		delete result;
		result = NULL;
	}
	return true;
}

bool CMorphologicalProc::MinKowskisum(ModelOBJ* model,double operSize /* = 1.0 */)
{

	//minkowIKi sum
	int Solid_no = 0;
	for (SOLID_itr solid_itr = model->begin(); solid_itr != model->end(); solid_itr++)
	{
		SolidOBJ* solid = *solid_itr;
		if (solid->Solid_mesh->size_of_vertices() == 0 || !solid->Solid_mesh->is_closed())
		{
			continue;
		}

		//kernel conversion IK to EK
		EK_Poly_3 ek_poly;
		if (!CKernelConverter::GetSingleton()->IK_to_EK(solid->Solid_mesh, &ek_poly))
		{
			return false;
		}

		//
		Nef_polyhedron_3* result = new Nef_polyhedron_3(ek_poly);
		std::cout<<"Solid:"<<Solid_no++<<std::endl;
		result = MinKowskisum(result,operSize);

		if (!result)
		{
			return false;
		}

		if (result->is_simple())
		{
			result->convert_to_polyhedron(ek_poly);

			//kernel conversion EK to IK
			Polyhedron_3 polycpy;
			if (!CKernelConverter::GetSingleton()->EK_to_IK(&ek_poly, &polycpy))
			{
				return false;
			}

			Polyhedron_3* tmp = solid->Solid_mesh;
			Polyhedron_3* poly = new Polyhedron_3(polycpy);
			solid->Solid_mesh = poly;
			delete tmp;
			tmp = NULL;
		}
		else
		{
			std::cerr<<"minkowski sum cause non-2-manifold case"<<std::endl;
		}
		//
		delete result;
		result = NULL;
	}
	return true;
}